//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "xllp_mmc.h"
#include "sderr.h"
#include "ddkit.h"
/******************************************************************************
Description: sets the alternate functions for GPIO and turns the mmc clock on
  				in the clock controller
*******************************************************************************/
void XllpMmcSdHWInit( P_XLLP_GPIO_T GPIOReg,
                      P_XLLP_CLKMGR_T CLKReg,
                      P_XLLP_BCR_T BCRReg )
{
    // configurate gpio function
    GPIOReg->GPDR1 &= ~(0x1<<3);    //1:SD PLUG 0:SD LEFT
    GPIOReg->GAFR1_L &=~0x000000C0;
    GPIOReg->GPDR2 |= 0x1<<15;
    GPIOReg->GAFR2_L &= ~0xC0000000;
    GPIOReg->GPDR1 |= 0x00000001;

    GPIOReg->GAFR1_L = ( (GPIOReg->GAFR1_L & ~0x00000003) | 0x00000002 ); // GPIO32:AF2
    GPIOReg->GAFR2_U = ( (GPIOReg->GAFR2_U & ~0x03000000) | 0x01000000 ); // GPIO92:AF1
    GPIOReg->GAFR3_L = ( (GPIOReg->GAFR3_L & ~0xFC000000) | 0x54000000 ); // GPIO109,110,111:AF1
    GPIOReg->GAFR3_U = ( (GPIOReg->GAFR3_U & ~0x00000003) | 0x00000001 ); // GPIO112:AF1

    GPIOReg->GPSR2 |=0x1<<15; //GPIO79 High

    //  enabling SD/MMC clock
	CLKReg->cken |= XLLP_CLKEN_MMC;

}

void XllpMmcSdHwShutdown( P_XLLP_CLKMGR_T CLKReg,
                          P_XLLP_BCR_T BCRReg )
{
 // turns off the power from the board level
//    BcrReg->MISCWR1 &= ~(XLLP_BCR_MISCWR1_MMC_ON);
    CLKReg->cken &= ~XLLP_CLKEN_MMC;

}

/******************************************************************************
  Description:  returns the response out of the fifo with the response
				structure filled in appropriately
*******************************************************************************/
XLLP_UINT32_T XllpMmcXtractResp( P_XLLP_MMC_T SDMMCRegs,
                        P_XLLP_UINT16_T buff,
                        XLLP_UINT32_T TimeOut )
{
    XLLP_UINT32_T  dtmp, dtimer, count, idx;
    P_XLLP_UINT8_T resb = (P_XLLP_UINT8_T)buff;
    /* Timer count for an MMC response */
    dtimer = TimeOut;
    count = 6;

    if ( (SDMMCRegs->MMC_CMDAT & 0x3) == XLLP_MMC_MMCSPIR2 ) {
        count = 17;
    }
    while ( dtimer ) {
        /* Read status and wait for proper response */
		dtmp = SDMMCRegs->MMC_STAT;
        dtimer--;

        /* Check for TIME OUT on response */
        if ( dtmp & XLLP_STAT_TORSPNS ) {
            return SD_API_STATUS_RESPONSE_TIMEOUT;
        }
        /* Check for TIME OUT on Receive Data */
        else if ( dtmp & XLLP_STAT_TORD ) {
            return SD_API_STATUS_DATA_TIMEOUT;
        }
        /* Check for CRC on response */
        else if ( dtmp & XLLP_STAT_RESCRCERR) {
            return SD_API_STATUS_RES_CRC_ERROR;
        }
        else if ( dtmp & XLLP_STAT_CRCWRERR ) {
            return SD_API_STATUS_WD_CRC_ERROR;
        }
        /* Check for CRC read error */
        else if ( dtmp & XLLP_STAT_CRCRDERR ) {
            return SD_API_STATUS_RD_CRC_ERROR;
        }
        /* Check for command response */
        else if ( dtmp & XLLP_STAT_ENDCMDRES ) {
            for (idx = 0; idx < count;) {
                dtmp = SDMMCRegs->MMC_RES;
                resb[idx++] = (XLLP_UINT8_T)(dtmp >> 8);
                if ( idx < count ) {
                    resb[idx++] = (XLLP_UINT8_T)(dtmp & 0xFF);
                }
            }
            return 0;
        }

    }

    return SD_API_STATUS_CARD_IS_NOT_RESPONDING;//MMC_CARD_IS_NOT_RESPONDING
}

/******************************************************************************
Description:  sets the clock rate and starts the clock
*******************************************************************************/
XLLP_BOOL_T XllpMmcSetUpClock( P_XLLP_MMC_T SDMMCRegs,
                        XLLP_MMC_CLKRATE rate,
                        XLLP_BOOL_T strClck )
{
    XLLP_BOOL_T ret = XLLP_TRUE;
    SDMMCRegs->MMC_CLKRT = rate;
    if (strClck) {
        ret = XllpMmcStartClock(SDMMCRegs);
    }
    return ret;
}

XLLP_BOOL_T XllpMmcStartClock( P_XLLP_MMC_T SDMMCRegs )
{
    XLLP_INT32_T  dTime;
    SDMMCRegs->MMC_STRPCL = XLLP_STRPCL_STRTCLK;
    dTime = 0x2000000;
    return XllpMmcSdStatus( SDMMCRegs, XLLP_STAT_CLKEN, dTime, XLLP_TRUE);
}

XLLP_BOOL_T XllpMmcStopClock( P_XLLP_MMC_T SDMMCRegs )
{
    XLLP_INT32_T  dTime;
    /* Disable MMC clock. */
 	SDMMCRegs->MMC_STRPCL = XLLP_STRPCL_STPCLK;
    dTime = 0x2000000;
    return XllpMmcSdStatus( SDMMCRegs,XLLP_STAT_CLKEN, dTime, XLLP_FALSE);
}

XLLP_BOOL_T XllpMmcSdPrepareAndSetup( P_XLLP_MMC_T SDMMCRegs,
                                      XLLP_MMC_CMD Cmd,
        							  XLLP_INT32_T Arg,
        							  XLLP_INT16_T noBlocks,
        							  XLLP_INT16_T Resp,
        							  XLLP_BOOL_T xMode )
{
	if( (Cmd == XLLP_MMC_CMD11) ||
		(Cmd == XLLP_MMC_SPICMD17) ||
		(Cmd == XLLP_MMC_CMD18) ||
		(Cmd == XLLP_SD_ACMD13 ) ||
		(Cmd == XLLP_SD_ACMD51) ||
  		(Cmd == XLLP_MMC_CMD20) ||
		(Cmd == XLLP_MMC_SPICMD24) ||
		(Cmd == XLLP_MMC_CMD25) ) {
	if(Cmd == XLLP_SD_ACMD13)
	 	 XllpMmcSdSetupXCmd(SDMMCRegs, (XLLP_INT32_T)(512 >> 3), (XLLP_INT32_T)(512 >> 3));
   	else
   		if(Cmd == XLLP_SD_ACMD51)
   			 XllpMmcSdSetupXCmd(SDMMCRegs, (XLLP_INT32_T)8, (XLLP_INT32_T)8);
   		else {
  			 XllpMmcSdSetupXCmd(SDMMCRegs, (XLLP_INT32_T)(noBlocks * 512), (XLLP_INT32_T)512);
	    }
	}

    XllpMmcSdSetupCmd(SDMMCRegs, Cmd, Arg, 64, xMode);

    if  ( ( SDMMCRegs->MMC_CMDAT & XLLP_MMC_CMDAT_WRRD ) != XLLP_MMC_CMDAT_WRRD) {
        SDMMCRegs->MMC_RDTO	= 0xFFFF;
    }
    return XLLP_TRUE;
}


/******************************************************************************
Description:  puts the values the driver wants into the registers and
				turns on the clock
*******************************************************************************/
XLLP_BOOL_T XllpMmcSdSetupCmd( P_XLLP_MMC_T SDMMCRegs,
                               XLLP_MMC_CMD cmd,
							   XLLP_INT32_T arg,
							   XLLP_INT32_T TimeOut,
							   XLLP_BOOL_T fourBitMode )
{
    XLLP_BOOL_T dataTrans = XLLP_FALSE;
    XLLP_UINT16_T MMCCMDAT_MASK = 0x0;
 //  read command of any type
	SDMMCRegs->MMC_CMD = cmd;
	SDMMCRegs->MMC_ARGH = (arg >> 16);
	SDMMCRegs->MMC_ARGL = (arg & 0xffff);
	SDMMCRegs->MMC_RESTO = TimeOut;

    if (fourBitMode) {
        MMCCMDAT_MASK |= XLLP_MMC_CMDAT_SD4DAT;
    }

    if ( (cmd == XLLP_MMC_CMD11)  || ( cmd == XLLP_MMC_SPICMD17) ||
	  	 (cmd == XLLP_MMC_CMD18)  || ( cmd == XLLP_SD_ACMD13 )   ||
	  	 (cmd == XLLP_SD_ACMD51) ) {
        if (cmd == XLLP_MMC_CMD11) {//  stream bit set for stream command
            MMCCMDAT_MASK |= (XLLP_MMC_CMDAT_DATAEN | XLLP_MMC_MMCSPIR1 | XLLP_MMC_CMDAT_STRMBLK);
	    }
	    else {
            MMCCMDAT_MASK |= (XLLP_MMC_CMDAT_DATAEN | XLLP_MMC_MMCSPIR1 );
	    }
        dataTrans = XLLP_TRUE;
    } //  write command of any type
    else if ( (cmd == XLLP_MMC_CMD20) || (cmd == XLLP_MMC_SPICMD24) ||
	  	      (cmd == XLLP_MMC_CMD25) ) {
        if (cmd == XLLP_MMC_CMD20) {//  stream bit set for stream command
	        MMCCMDAT_MASK |= ( XLLP_MMC_CMDAT_DATAEN | XLLP_MMC_CMDAT_WRRD |
	                            XLLP_MMC_MMCSPIR1 | XLLP_MMC_CMDAT_STRMBLK );
	    }
	    else {
            MMCCMDAT_MASK |= (XLLP_MMC_CMDAT_DATAEN | XLLP_MMC_CMDAT_WRRD | XLLP_MMC_MMCSPIR1);
        }
        dataTrans = XLLP_TRUE;
    }//  command that requires a busy bit
    else if( (cmd == XLLP_MMC_CMD7 )||  (cmd == XLLP_MMC_CMD12 )    ||
        (cmd == XLLP_MMC_SPICMD28 ) ||  (cmd == XLLP_MMC_SPICMD29)  ||
        (cmd == XLLP_MMC_SPICMD38 ) ||  (cmd == XLLP_MMC_SPICMD42)  ||
        (cmd == XLLP_MMC_SPICMD56) ) {
        MMCCMDAT_MASK |= ( XLLP_MMC_CMDAT_BUSY | XLLP_MMC_MMCSPIR1) ;
    }// command for a response type of 2
    else if ( (cmd == XLLP_MMC_CMD2) ||	(cmd == XLLP_MMC_SPICMD9) ||
              (cmd == XLLP_MMC_SPICMD10) ) {
	  	MMCCMDAT_MASK |= XLLP_MMC_MMCSPIR2;
    }// command for a response type of 3
    else if ((cmd == XLLP_MMC_SPICMD1)|| (cmd == XLLP_SD_ACMD41)) {
        MMCCMDAT_MASK |= XLLP_MMC_MMCR3;
    }
    else if (cmd == XLLP_MMC_SPICMD0)	{
    //command 0 for init the card to start the 80 clock cycles
        MMCCMDAT_MASK |= (XLLP_MMC_CMDAT_INIT | XLLP_MMC_NORESPONSE);
    }
    else if (( cmd == XLLP_MMC_CMD4 ) | ( cmd == XLLP_MMC_CMD15 )) {
        MMCCMDAT_MASK |= XLLP_MMC_NORESPONSE;
    }
    else if ( (cmd ==XLLP_MMC_SPICMD55) ) {
        MMCCMDAT_MASK |= XLLP_MMC_CMDAT_BUSY;
    }
    else// all other commands are response 1 with no other special cases
	    MMCCMDAT_MASK |= XLLP_MMC_MMCSPIR1;

    if  ( !( MMCCMDAT_MASK & XLLP_MMC_CMDAT_WRRD )) {
        SDMMCRegs->MMC_RDTO	= 0xFFFF;
    }

    SDMMCRegs->MMC_CMDAT = MMCCMDAT_MASK;

    return(dataTrans);

}

/******************************************************************************
  Description:  puts the values the driver wants into the registers and
				turns on the clock
*******************************************************************************/
XLLP_BOOL_T XllpMmcSdSetupXCmd( P_XLLP_MMC_T SDMMCRegs,
                                XLLP_INT32_T FlSz,
							    XLLP_INT32_T BlkSz)

{
 //  read command of any type
    SDMMCRegs->MMC_NUMBLK = FlSz/BlkSz;
    SDMMCRegs->MMC_BLKLEN = BlkSz;

    return XLLP_FALSE;

}


/******************************************************************************
Description:  allows the user the pass in a bit defined (setBit) and then the function
				turns the value around and masks it off appropriately  or clears a bit for
				interrupts
*******************************************************************************/
void XllpMmcSdInts(P_XLLP_MMC_T SDMMCRegs, XLLP_INT8_T SetMask)
{
    SDMMCRegs->MMC_I_MASK = ~SetMask;
}

XLLP_BOOL_T XllpMmcSdStatus( P_XLLP_MMC_T SDMMCRegs,
                             XLLP_INT32_T Status,
                             XLLP_INT32_T TimeOut,
                             XLLP_BOOL_T BitOne )
{
    XLLP_INT32_T dStatus, tOut;
    XLLP_BOOL_T RetVal = XLLP_FALSE;
    tOut = TimeOut;

    while(tOut--) {
        dStatus = SDMMCRegs->MMC_STAT;
        if (BitOne) {
            if ( dStatus & Status ) {
                RetVal = XLLP_TRUE;
                break;
            }
        }
        else {
            if ( !(dStatus & Status) ) {
                RetVal = XLLP_TRUE;
                break;
            }
        }

    }
    return RetVal;
}


